library apache;

include <core.vhs>
include <netutil.vhs>

import core::*;
import core::Pattern::*;
import core::Logging::*;
import core::JobEngine::*;
import netutil::*;

pattern<DataPart> ApacheAdvisories($dataPattern=@'href="(security-\d+\.html)"', $host = 'http://tomcat.apache.org/', $outfunc = null, $outfunc_arg) {
	queueJob(function(url, outfunc, ...) {
		println("Process url: %s", url);
		var filename = download(url, SEC_PER_MONTH);
		ApacheAdvisory(input = new SequenceStream(filename, 4096), outfunc=outfunc, outfunc_arg=outfunc_arg);
	}, url = host + this[1], outfunc = outfunc, outfunc_arg=outfunc_arg);
}

pattern ApacheAdvisory($maxOccur=INFINITY, $outfunc, $outfunc_arg) {
	locate(@'<strong>Fixed in (.+?) (\d+(\.\d+)*)</strong>', groups=struct { FixedVersion = 2 });	
	var fixedVersion = this.begin.FixedVersion;
	locate('<tr>')	
	locate_end('tr', operator=LOCATOR_CLOSE_TAG);
	ApacheVulnerability(fixedVersion = fixedVersion, outfunc=outfunc, outfunc_arg=outfunc_arg);
}

pattern ApacheVulnerability(
		$fixedVersion = null,
		$outfunc,
		$outfunc_arg,
		$maxOccur=INFINITY,
		$patTitle = @'<strong>(.+?)</strong>.+?name=(CVE-\d+-\d+)',
		$patAffect = @'<p>Affects: (.+?)</p>' 
	) {
	var title = locate(patTitle, groups = struct { Title = 1, CVE = 2});
	locate_end(patAffect, groups = struct {AffectVersions = 1});
	
	var reportedDate = '';
	var reportedDatePat;
	if (reportedDatePat = ApacheReportDateWithReporter())
		reportedDate = reportedDatePat.ReportedDate
	else if (reportedDatePat = ApacheReportDateWithoutReporter())
		reportedDate = reportedDatePat.ReportedDate
	
	outfunc?(struct {CVE = title.CVE, Title = title.Title.trim().replace('\n', ''), 
		AffectVersions = this.end.AffectVersions, FixedVersion = fixedVersion, ReportedDate = reportedDate}, outfunc_arg);
}

//identified/discovered by <reporter> on <reported-date>
pattern<DataPart> ApacheReportDateWithReporter($dataPattern=@'(identified|discovered) by (.+?) on (\d+ \w+ \d+)', $minOccur=0, $groups = struct {ReportedDate = 3, Reporter=2}) {}
pattern<DataPart> ApacheReportDateWithoutReporter($dataPattern=@'first reported to (.+?) on (\d+ \w+ \d+)', $minOccur=0, $groups= struct {ReportedDate = 2, Reporter=1}) {}